import base64
import json
from rest_framework import viewsets, generics, permissions, status, filters
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework_simplejwt.tokens import RefreshToken
from rest_framework_simplejwt.views import TokenObtainPairView
from rest_framework_simplejwt.exceptions import TokenError
from rest_framework.parsers import MultiPartParser, FormParser
from django.contrib.auth.hashers import make_password
from django.db import transaction
from django.db.models import Q
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from .models import Category, CulturalRelic, DiseaseImage, User
# ImageRecordImage, ImageRecordLineDrawing, User,Survey, Building, PaintingLocation, PaintingContent, MaterialTechnique, preservationAssessment, HistoricalRecord, ImageRecord, PaintingLocationImage
from .serializers import (
    CategorySerializer, CulturalRelicSerializer, CustomTokenObtainPairSerializer, RefreshTokenSerializer, UserSerializer, RegisterSerializer,
    ChangePasswordSerializer, ChoicesSerializer, )
from .authentication.permissions import IsAdminUser
from PIL import Image, ImageFile
from io import BytesIO
import os
import mimetypes
from urllib.parse import quote
from django.http import HttpResponse, FileResponse
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from rest_framework.permissions import IsAuthenticated
from .utils.compare_images import compare_images  # 路径根据你项目结构调整
from rest_framework.decorators import api_view
from django.utils import timezone
from traceback import print_exc
from django.core.exceptions import ObjectDoesNotExist
import logging


def format_serializer_errors(errors):
    """
    展开序列化器校验错误，将嵌套的错误信息平铺返回
    """
    formatted_errors = {}

    for field, error_list in errors.items():
        if isinstance(error_list, list):
            # 如果是字段错误列表，直接取第一个错误或合并为字符串
            if len(error_list) == 1:
                formatted_errors[field] = str(error_list[0])
            else:
                formatted_errors[field] = '; '.join([str(err) for err in error_list])
        elif isinstance(error_list, dict):
            # 如果是嵌套字段错误，递归处理
            nested_errors = format_serializer_errors(error_list)
            for nested_field, nested_error in nested_errors.items():
                formatted_errors[f"{field}.{nested_field}"] = nested_error
        else:
            # 其他情况，直接转换为字符串
            formatted_errors[field] = str(error_list)

    return formatted_errors


@api_view(["POST"])
def compare_disease_images(request):
    """
    接收 relic_id 和 image_ids: [id1, id2]
    返回相似度和差异图（base64）
    """
    relic_id = request.data.get("relic_id")
    image_ids = request.data.get("image_ids", [])
    if not relic_id or len(image_ids) != 2:
        return Response({
            "error": "参数不足",
            "message": "需 relic_id 和两个 image_ids",
            "code": "INVALID_PARAMS"
        }, status=400)

    try:
        relic = CulturalRelic.objects.get(id=relic_id)
        image1 = DiseaseImage.objects.get(id=image_ids[0], relic=relic).image
        image2 = DiseaseImage.objects.get(id=image_ids[1], relic=relic).image
    except CulturalRelic.DoesNotExist:
        return Response({
            "error": "文物不存在",
            "message": "指定的文物记录不存在",
            "code": "RELC_NOT_FOUND"
        }, status=404)
    except DiseaseImage.DoesNotExist:
        return Response({
            "error": "图像不存在",
            "message": "图像不存在或不属于该文物",
            "code": "IMAGE_NOT_FOUND"
        }, status=404)

    # 执行比对
    try:
        similarity, diff_base64 = compare_images(image1.path, image2.path)
        similarity = float(similarity)
    except Exception as e:
        return Response({
            "error": "比对失败",
            "message": f"比对过程中发生错误: {str(e)}",
            "code": "COMPARE_FAILED"
        }, status=500)

    return Response({
        "similarity": similarity,
        "diff_image_base64": f"data:image/jpeg;base64,{diff_base64}"
    })


class RegisterAPI(generics.GenericAPIView):
    serializer_class = RegisterSerializer
    permission_classes = [permissions.AllowAny]

    def post(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)
        try:
            serializer.is_valid(raise_exception=True)
        except Exception:
            formatted_errors = format_serializer_errors(serializer.errors)
            return Response({
                "error": "验证失败",
                "message": "提供的数据格式不正确",
                "code": "VALIDATION_ERROR",
                "details": formatted_errors
            }, status=400)

        user = serializer.save()

        return Response({
            "user": UserSerializer(user, context=self.get_serializer_context()).data,
            "message": "用户注册成功"
        })

    def get_client_ip(self, request):
        x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
        if x_forwarded_for:
            ip = x_forwarded_for.split(',')[0]
        else:
            ip = request.META.get('REMOTE_ADDR')
        return ip


class RefreshTokenAPI(generics.GenericAPIView):
    serializer_class = RefreshTokenSerializer
    permission_classes = [permissions.AllowAny]

    def post(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)
        if not serializer.is_valid():
            formatted_errors = format_serializer_errors(serializer.errors)
            return Response({
                "error": "验证失败",
                "message": "请求数据格式不正确",
                "code": "VALIDATION_ERROR",
                "details": formatted_errors
            }, status=status.HTTP_400_BAD_REQUEST)

        refresh_token = serializer.validated_data.get('refresh')

        try:
            # 验证 refresh token
            refresh = RefreshToken(refresh_token)

            # 生成新的 access token
            access_token = refresh.access_token
            print("刷新token了")

            return Response({
                "access": str(access_token),
                "refresh": str(refresh)
            }, status=status.HTTP_200_OK)

        except TokenError:
            return Response({
                "error": "令牌无效",
                "message": "您的刷新令牌无效或已过期",
                "code": "TOKEN_ERROR"
            }, status=status.HTTP_401_UNAUTHORIZED)


class LoginAPI(TokenObtainPairView):
    serializer_class = CustomTokenObtainPairSerializer
    permission_classes = [permissions.AllowAny]

    def post(self, request, *args, **kwargs):
        # 这将自动使用你的CustomTokenObtainPairSerializer
        return super().post(request, *args, **kwargs)


class UserAPI(generics.RetrieveAPIView):
    permission_classes = [permissions.IsAuthenticated]
    serializer_class = UserSerializer

    def get_object(self):
        return self.request.user


class ChangePasswordAPI(generics.GenericAPIView):
    serializer_class = ChangePasswordSerializer
    permission_classes = [permissions.IsAuthenticated]

    def post(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)
        try:
            serializer.is_valid(raise_exception=True)
        except Exception:
            formatted_errors = format_serializer_errors(serializer.errors)
            return Response({
                "error": "验证失败",
                "message": "提供的数据格式不正确",
                "code": "VALIDATION_ERROR",
                "details": formatted_errors
            }, status=400)

        # 检查旧密码
        user = request.user
        if not user.check_password(serializer.validated_data['old_password']):
            return Response({
                "error": "密码不正确",
                "message": "当前密码输入错误",
                "code": "PASSWORD_INCORRECT"
            }, status=status.HTTP_400_BAD_REQUEST)

        # 设置新密码
        user.set_password(serializer.validated_data['new_password'])
        user.save()

        return Response({
            "message": "密码修改成功"
        }, status=status.HTTP_200_OK)

    def get_client_ip(self, request):
        x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
        if x_forwarded_for:
            ip = x_forwarded_for.split(',')[0]
        else:
            ip = request.META.get('REMOTE_ADDR')
        return ip


class UserViewSet(viewsets.ModelViewSet):
    queryset = User.objects.all()
    serializer_class = UserSerializer
    permission_classes = [IsAdminUser]

    def get_permissions(self):
        if self.action == 'retrieve':
            self.permission_classes = [permissions.IsAuthenticated]
        return super().get_permissions()

    def perform_create(self, serializer):
        password = make_password(serializer.validated_data['password'])

        user = serializer.save(password=password)

    def perform_update(self, serializer):
        user = serializer.save()


def compress_image(image_field, max_size=(600, 600), quality=60):
    """压缩图片并返回base64编码字符串"""
    try:
        with open(image_field.path, 'rb') as f:
            img = Image.open(f)
            img.thumbnail(max_size, Image.Resampling.LANCZOS)  # 调整尺寸

            output_buffer = BytesIO()
            filename = image_field.name.lower()
            content_type = 'image/jpeg'

            # 根据图片格式处理
            if filename.endswith('.png'):
                img.save(output_buffer, format='PNG', compress_level=min(9, quality // 10))
                content_type = 'image/png'
            elif filename.endswith('.gif'):
                img.save(output_buffer, format='GIF')
                content_type = 'image/gif'
            else:
                if img.mode != 'RGB':
                    img = img.convert('RGB')
                img.save(output_buffer, format='JPEG', quality=quality)

            # 转换为base64
            compressed_data = output_buffer.getvalue()
            base64_str = base64.b64encode(compressed_data).decode('utf-8')
            return f"data:{content_type};base64,{base64_str}"

    except Exception as e:
        print(f"图片处理错误（{image_field.name}）: {str(e)}")
        return None
    finally:
        if 'img' in locals():
            img.close()  # 释放图片资源


class VerifyTokenView(APIView):
    permission_classes = [permissions.IsAuthenticated]

    def get(self, request):
        user = request.user
        user_info = {
            'id': user.id,
            'username': user.username,
            'role': user.role,  # 这里需要替换为实际的角色字段
            'email': user.email,
            'name': user.name
        }
        return Response({'user': user_info})


class ChoicesView(APIView):
    def get(self, request):
        serializer = ChoicesSerializer({})
        return Response(serializer.data)


class CategoryViewSet(viewsets.ModelViewSet):
    """
    一级类别管理API：
    - GET /api/categories/：获取所有类别（支持分页、搜索，返回count/pages/data）
    - POST /api/categories/：新增类别（仅管理员）
    - ... 其他操作 ...
    """
    queryset = Category.objects.all()
    serializer_class = CategorySerializer
    permission_classes = [IsAuthenticated]
    filter_backends = [filters.SearchFilter, filters.OrderingFilter]
    search_fields = [
        'name',
        'description',
        'address',
        'construction_age',
        'construction_type',
        'layout_orientation',
        'protection_level'
    ]  # 支持新字段搜索
    ordering_fields = [
        'name',
        'created_at',
    ]  # 支持按年代排序

    def get_permissions(self):
        """权限控制：仅管理员可修改"""
        if self.action in ['create', 'update', 'partial_update', 'destroy']:
            self.permission_classes = [IsAdminUser]
        return super().get_permissions()

    def destroy(self, request, *args, **kwargs):
        """删除前清理图片文件"""
        instance = self.get_object()
        if instance.image:
            try:
                if os.path.exists(instance.image.path):
                    os.remove(instance.image.path)
            except Exception as e:
                logger = logging.getLogger(__name__)
                logger.error(f"删除类别图片失败: {str(e)}")
        return super().destroy(request, *args, **kwargs)

    def list(self, request, *args, **kwargs):
        """重写列表方法，返回标准化分页格式"""
        try:
            queryset = self.filter_queryset(self.get_queryset())

            page = request.GET.get('page', 1)
            page_size = request.GET.get('page_size', 10)
            paginator = Paginator(queryset, page_size)

            try:
                categories = paginator.page(page)
            except PageNotAnInteger:
                categories = paginator.page(1)
            except EmptyPage:
                categories = paginator.page(paginator.num_pages)

            serializer = self.get_serializer(categories, many=True)
            # ✅ CompressedImageField 自动处理 image_base64
            results = serializer.data

            return Response({
                'count': paginator.count,
                'pages': paginator.num_pages,
                'data': results
            }, status=status.HTTP_200_OK)

        except Exception as e:
            return Response(
                {'error': str(e)},
                status=status.HTTP_400_BAD_REQUEST
            )

    def retrieve(self, request, *args, **kwargs):
        """获取单个类别详情"""
        try:
            instance = self.get_object()
            serializer = self.get_serializer(instance)
            return Response(serializer.data, status=status.HTTP_200_OK)
        except Exception as e:
            return Response(
                {'error': str(e)},
                status=status.HTTP_400_BAD_REQUEST
            )

    def create(self, request, *args, **kwargs):
        """重写创建方法，返回错误信息字符串"""
        try:
            return super().create(request, *args, **kwargs)
        except Exception as e:
            return Response(
                {'error': str(e)},
                status=status.HTTP_400_BAD_REQUEST
            )

    def update(self, request, *args, **kwargs):
        """重写更新方法，返回错误信息字符串"""
        try:
            return super().update(request, *args, **kwargs)
        except Exception as e:
            return Response(
                {'error': str(e)},
                status=status.HTTP_400_BAD_REQUEST
            )

    def partial_update(self, request, *args, **kwargs):
        """重写部分更新方法，返回错误信息字符串"""
        try:
            return super().partial_update(request, *args, **kwargs)
        except Exception as e:
            return Response(
                {'error': str(e)},
                status=status.HTTP_400_BAD_REQUEST
            )


class CulturalRelicUploadView(APIView):
    permission_classes = [permissions.IsAuthenticated]
    parser_classes = [MultiPartParser, FormParser]

    @transaction.atomic
    def post(self, request):
        try:
            # 构建 form_data
            form_data = {
                "name": request.data.get("name"),
                "image": request.FILES.get("image"),
                "dynasty": request.data.get("dynasty", ""),
                "country": request.data.get("country"),
                "building_type": request.data.get("building_type"),
                "other_building_type": request.data.get("other_building_type"),
                "category_id": request.data.get("category_id"),

                # 病害相关
                "disease_types": request.data.get("disease_types", "").strip(),
                "other_disease_types": request.data.get("other_disease_types"),
                "disease_severity": request.data.get("disease_severity"),

                # 艺术特征字段
                "pattern_types": request.data.get("pattern_types"),
                "painting_theme": request.data.get("painting_theme"),
                "composition_style": request.data.get("composition_style"),
                "dominant_colors": request.data.get("dominant_colors"),
                "painting_technique": request.data.get("painting_technique"),
                "current_damage_status": request.data.get("current_damage_status"),

                # 文件上传
                "hasSpectrum": request.data.get("hasSpectrum") in ['true', 'True', '1'],
                "spectrum_file": request.FILES.get("spectrum_file"),
                "hasThreeD": request.data.get("hasThreeD") in ['true', 'True', '1'],
                "threeD_file": request.FILES.get("threeD_file"),
            }

            # 反序列化并验证
            serializer = CulturalRelicSerializer(data=form_data)
            if not serializer.is_valid():
                formatted_errors = format_serializer_errors(serializer.errors)
                return Response({
                    "error": "数据验证失败",
                    "message": "提交的数据格式不正确",
                    "code": "VALIDATION_ERROR",
                    "details": formatted_errors
                }, status=status.HTTP_400_BAD_REQUEST)

            # 保存文物实例，自动设置创建者和修改者
            relic = serializer.save(created_by=request.user, last_modified_by=request.user)

            # 处理病害图像
            disease_images = request.FILES.getlist('disease_images')
            metadata_str = request.POST.get('disease_image_metadata', '[]')

            try:
                metadata_list = json.loads(metadata_str)
                if not isinstance(metadata_list, list):
                    metadata_list = []
            except json.JSONDecodeError as e:
                return Response({
                    'error': '元数据格式错误',
                    'message': 'disease_image_metadata 格式错误，必须为 JSON 数组',
                    'code': 'JSON_DECODE_ERROR',
                    'details': str(e)
                }, status=status.HTTP_400_BAD_REQUEST)

            # 校验文件与元数据数量匹配
            if len(disease_images) != len(metadata_list):
                return Response({
                    'error': '数据不匹配',
                    'message': f'文件数量 ({len(disease_images)}) 与元数据数量 ({len(metadata_list)}) 不匹配',
                    'code': 'DATA_MISMATCH'
                }, status=status.HTTP_400_BAD_REQUEST)

            # 创建 DiseaseImage 记录
            for i, image_file in enumerate(disease_images):
                meta = metadata_list[i]
                image_name = meta.get('name', '').strip() if isinstance(meta.get('name'), str) else ''
                description = meta.get('description', '').strip() if isinstance(meta.get('description'), str) else ''

                DiseaseImage.objects.create(
                    relic=relic,
                    image=image_file,
                    image_name=image_name,
                    description=description
                )

            # 准备响应数据
            response_data = serializer.data

            # 获取第一个病害图像用于预览（可选）
            first_disease_image = relic.disease_images.first()
            if first_disease_image:
                response_data['disease_image'] = first_disease_image.image.url
                # 可选：添加 base64 缩略图
                compressed = compress_image(first_disease_image.image, max_size=(600, 600), quality=60)
                if compressed:
                    response_data['disease_image_base64'] = compressed

            return Response({
                "message": "文物信息保存成功",
                "data": response_data
            }, status=status.HTTP_201_CREATED)

        except Exception as e:
            # 出现异常时回滚事务
            transaction.set_rollback(True)
            return Response({
                "error": "服务器内部错误",
                "message": "服务器处理请求时发生错误",
                "code": "INTERNAL_ERROR",
                "details": str(e)
            }, status=status.HTTP_500_INTERNAL_SERVER_ERROR)


class CulturalRelicListView(APIView):
    permission_classes = [permissions.IsAuthenticated]

    def get(self, request):
        category_id = request.GET.get('category_id')
        if not category_id:
            return Response({
                "error": "参数缺失",
                "message": "缺少必要参数：category_id",
                "code": "MISSING_PARAM"
            }, status=status.HTTP_400_BAD_REQUEST)

        # 权限筛选
        if request.user.role == 'admin':
            queryset = CulturalRelic.objects.all()
        else:
            queryset = CulturalRelic.objects.filter(created_by=request.user)

        # 过滤类别
        queryset = queryset.filter(category_id=category_id)

        # 搜索支持更多字段
        search = request.GET.get('search', '').strip()
        if search:
            queryset = queryset.filter(
                Q(name__icontains=search) |
                Q(painting_theme__icontains=search)
            )

        # 分页
        page = request.GET.get('page', 1)
        page_size = request.GET.get('page_size', 10)
        paginator = Paginator(queryset, page_size)

        try:
            relics = paginator.page(page)
        except PageNotAnInteger:
            relics = paginator.page(1)
        except EmptyPage:
            relics = paginator.page(paginator.num_pages)

        serializer = CulturalRelicSerializer(relics, many=True)
        results = serializer.data  # ✅ 自动包含 image_base64

        # 处理病害图片 base64（可选增强）
        for i, relic in enumerate(relics):
            relic_data = results[i]
            first_disease_image = relic.disease_images.first()
            if first_disease_image:
                # 若需返回病害图 base64，可调用 compress_image 或自定义字段
                compressed = compress_image(
                    image_field=first_disease_image.image,
                    max_size=(600, 600),
                    quality=60
                )
                if compressed:
                    relic_data['disease_image_base64'] = compressed

        return Response({
            'count': paginator.count,
            'pages': paginator.num_pages,
            'data': results
        }, status=status.HTTP_200_OK)


class PointCloudAPI(APIView):
    permission_classes = [IsAuthenticated]

    def get(self, request, relic_id):
        try:
            # 获取文物对象
            relic = CulturalRelic.objects.get(id=relic_id)

            # 权限校验
            if not request.user.role == 'admin' and relic.created_by != request.user:
                return Response({
                    "error": "权限不足",
                    "message": "无权访问该文物资源",
                    "code": "PERMISSION_DENIED"
                }, status=403)

            # 检查文件是否存在（保持你原有的判断逻辑）
            if not relic.threeD_file or not os.path.exists(relic.threeD_file.path):
                return Response({
                    "error": "文件不存在",
                    "message": "点云文件不存在",
                    "code": "FILE_NOT_FOUND"
                }, status=404)

            # 响应配置（完全保留原有逻辑，不修改任何响应头）
            content_type, _ = mimetypes.guess_type(relic.threeD_file.path)
            response = FileResponse(
                open(relic.threeD_file.path, 'rb'),
                content_type=content_type or 'application/octet-stream'
            )
            response['Content-Length'] = os.path.getsize(relic.threeD_file.path)

            # 不添加任何额外响应头，完全依赖全局CORS配置
            return response

        except CulturalRelic.DoesNotExist:
            return Response({
                "error": "文物不存在",
                "message": "指定的文物记录不存在",
                "code": "RELIC_NOT_FOUND"
            }, status=404)
        except Exception as e:
            return Response({
                "error": "加载失败",
                "message": f"加载文件时发生错误: {str(e)}",
                "code": "LOAD_FAILED"
            }, status=500)


class ImageDownloadView(APIView):
    permission_classes = [IsAuthenticated]

    def get(self, request, file_type, pk, image_id=None):
        try:
            relic = CulturalRelic.objects.get(id=pk)

            # 权限检查
            if not request.user.role == 'admin' and relic.created_by != request.user:
                return Response({
                    "error": "权限不足",
                    "message": "无权访问该文物资源",
                    "code": "PERMISSION_DENIED"
                }, status=status.HTTP_403_FORBIDDEN)

            # 根据文件类型获取对应的文件字段
            file_field = None
            if file_type == 'image':
                if not relic.image:
                    return Response({
                        "error": "文件不存在",
                        "message": "主图不存在",
                        "code": "FILE_NOT_FOUND"
                    }, status=status.HTTP_404_NOT_FOUND)
                file_field = relic.image

            # 处理病害图片下载（新增支持多图）
            elif file_type == 'disease_image':
                # 如果提供了image_id，下载指定的病害图片
                if image_id:
                    try:
                        disease_image = DiseaseImage.objects.get(id=image_id, relic=relic)
                        file_field = disease_image.image
                        original_filename = os.path.basename(file_field.name)
                    except DiseaseImage.DoesNotExist:
                        return Response({
                            "error": "文件不存在",
                            "message": "指定的病害图不存在",
                            "code": "IMAGE_NOT_FOUND"
                        }, status=status.HTTP_404_NOT_FOUND)
                # 不提供image_id则下载第一张（保持向后兼容）
                else:
                    first_disease = relic.disease_images.first()
                    if not first_disease:
                        return Response({
                            "error": "文件不存在",
                            "message": "病害图不存在",
                            "code": "IMAGE_NOT_FOUND"
                        }, status=status.HTTP_404_NOT_FOUND)
                    file_field = first_disease.image
                    original_filename = os.path.basename(file_field.name)
            else:
                return Response({
                    "error": "参数错误",
                    "message": "不支持的图片类型",
                    "code": "UNSUPPORTED_FILE_TYPE"
                }, status=status.HTTP_400_BAD_REQUEST)

            # 检查文件是否存在
            if not os.path.exists(file_field.path):
                return Response({
                    "error": "文件不存在",
                    "message": "图片文件不存在",
                    "code": "FILE_NOT_FOUND"
                }, status=status.HTTP_404_NOT_FOUND)

            # 获取原始文件名
            original_filename = os.path.basename(file_field.name)

            # 检测MIME类型
            content_type, _ = mimetypes.guess_type(file_field.path)
            if not content_type:
                content_type = 'image/jpeg'  # 默认图片类型

            # 创建文件响应
            response = FileResponse(
                open(file_field.path, 'rb'),
                content_type=content_type
            )

            # 设置Content-Disposition响应头（处理中文文件名）
            encoded_filename = quote(original_filename.encode('utf-8'))
            response['Content-Disposition'] = f'attachment; filename="{original_filename}"; filename*=UTF-8\'\'{encoded_filename}'

            # 设置其他响应头
            response['Content-Length'] = os.path.getsize(file_field.path)
            response['Content-Type'] = content_type

            return response

        except CulturalRelic.DoesNotExist:
            return Response({
                "error": "文物不存在",
                "message": "指定的文物记录不存在",
                "code": "RELIC_NOT_FOUND"
            }, status=status.HTTP_404_NOT_FOUND)
        except Exception as e:
            return Response({
                "error": "下载失败",
                "message": f"下载文件时发生错误: {str(e)}",
                "code": "DOWNLOAD_FAILED"
            }, status=status.HTTP_500_INTERNAL_SERVER_ERROR)


class FileDownloadView(APIView):
    permission_classes = [IsAuthenticated]

    def get(self, request, file_type, pk):
        try:
            relic = CulturalRelic.objects.get(id=pk)

            # 权限检查
            if not request.user.role == 'admin' and relic.created_by != request.user:
                return Response({
                    "error": "权限不足",
                    "message": "无权访问该文物资源",
                    "code": "PERMISSION_DENIED"
                }, status=status.HTTP_403_FORBIDDEN)

            # 根据文件类型获取对应的文件字段
            file_field = None
            if file_type == 'spectrum':
                if not relic.spectrum_file:
                    return Response({
                        "error": "文件不存在",
                        "message": "光谱文件不存在",
                        "code": "FILE_NOT_FOUND"
                    }, status=status.HTTP_404_NOT_FOUND)
                file_field = relic.spectrum_file

            elif file_type == '3d':
                if not relic.threeD_file:
                    return Response({
                        "error": "文件不存在",
                        "message": "三维文件不存在",
                        "code": "FILE_NOT_FOUND"
                    }, status=status.HTTP_404_NOT_FOUND)
                file_field = relic.threeD_file

            else:
                return Response({
                    "error": "参数错误",
                    "message": "不支持的文件类型",
                    "code": "UNSUPPORTED_FILE_TYPE"
                }, status=status.HTTP_400_BAD_REQUEST)

            # 检查文件是否存在
            if not os.path.exists(file_field.path):
                return Response({
                    "error": "文件不存在",
                    "message": "文件不存在",
                    "code": "FILE_NOT_FOUND"
                }, status=status.HTTP_404_NOT_FOUND)

            # 获取原始文件名 - 关键修复！
            # 方式1: 如果你有保存原始文件名的字段
            # original_filename = relic.spectrum_original_name if file_type == 'spectrum' else relic.threeD_original_name

            # 方式2: 从文件路径中提取，但要去掉Django添加的后缀
            file_path = file_field.name  # 这会给你类似 'spectrum_files/filename.txt'
            original_filename = os.path.basename(file_path)

            # 如果Django重命名了文件（添加了随机后缀），尝试恢复原名
            # 这种情况下可能需要在上传时保存原始文件名

            # 检测MIME类型
            content_type, _ = mimetypes.guess_type(file_field.path)
            if not content_type:
                content_type = 'application/octet-stream'

            # 创建文件响应
            response = FileResponse(
                open(file_field.path, 'rb'),
                content_type=content_type
            )

            # 关键：设置Content-Disposition响应头，包含原始文件名
            # 使用UTF-8编码处理中文文件名
            encoded_filename = quote(original_filename.encode('utf-8'))
            response['Content-Disposition'] = f'attachment; filename="{original_filename}"; filename*=UTF-8\'\'{encoded_filename}'

            # 设置其他响应头
            response['Content-Length'] = os.path.getsize(file_field.path)
            response['Content-Type'] = content_type

            return response

        except CulturalRelic.DoesNotExist:
            return Response({
                "error": "文物不存在",
                "message": "文物记录不存在",
                "code": "RELIC_NOT_FOUND"
            }, status=status.HTTP_404_NOT_FOUND)
        except Exception as e:
            return Response({
                "error": "下载失败",
                "message": f"下载文件时发生错误: {str(e)}",
                "code": "DOWNLOAD_FAILED"
            }, status=status.HTTP_500_INTERNAL_SERVER_ERROR)


class DiseaseImageDeleteView(APIView):
    """删除单张病害图片的接口"""
    permission_classes = [permissions.IsAuthenticated]

    @transaction.atomic
    def delete(self, request, pk, image_id):
        try:
            # 获取文物信息
            relic = CulturalRelic.objects.get(id=pk)

            # 权限检查（仅创建者和管理员可删除）
            if not request.user.role == 'admin' and relic.created_by != request.user:
                return Response({
                    "error": "权限不足",
                    "message": "无权执行删除操作",
                    "code": "PERMISSION_DENIED"
                }, status=status.HTTP_403_FORBIDDEN)

            # 获取要删除的病害图片
            try:
                disease_image = DiseaseImage.objects.get(id=image_id, relic=relic)
            except DiseaseImage.DoesNotExist:
                return Response({
                    "error": "文件不存在",
                    "message": "指定的病害图片不存在",
                    "code": "IMAGE_NOT_FOUND"
                }, status=status.HTTP_404_NOT_FOUND)

            # 保存图片路径用于后续删除文件
            image_path = disease_image.image.path

            # 先删除数据库记录
            disease_image.delete()

            # 再删除本地文件（异常处理，防止数据库删除后文件删除失败）
            try:
                if os.path.exists(image_path):
                    os.remove(image_path)
            except Exception as e:
                # 记录警告日志，但不影响数据库删除操作
                import logging
                logger = logging.getLogger(__name__)
                logger.warning(f"删除图片文件失败: {str(e)}, 路径: {image_path}")

            return Response({
                "message": "病害图片删除成功"
            }, status=status.HTTP_200_OK)

        except CulturalRelic.DoesNotExist:
            return Response({
                "error": "文物不存在",
                "message": "文物记录不存在",
                "code": "RELIC_NOT_FOUND"
            }, status=status.HTTP_404_NOT_FOUND)
        except Exception as e:
            return Response({
                "error": "删除失败",
                "message": f"删除操作失败: {str(e)}",
                "code": "DELETE_FAILED"
            }, status=status.HTTP_500_INTERNAL_SERVER_ERROR)


class CulturalRelicDetailView(APIView):
    """文物详情视图（GET/PUT/DELETE请求）"""
    permission_classes = [permissions.IsAuthenticated]

    def get(self, request, pk):
        """获取单个文物详情"""
        try:
            relic = CulturalRelic.objects.get(id=pk)

            # 权限检查
            if not request.user.role == 'admin' and relic.created_by != request.user:
                return Response({
                    "error": "权限不足",
                    "message": "无权访问该文物资源",
                    "code": "PERMISSION_DENIED"
                }, status=403)

            serializer = CulturalRelicSerializer(relic)
            data = serializer.data

            # 处理图片（压缩后返回base64，用于预览）
            if relic.image:
                try:
                    # 压缩文物主图
                    compressed_data = self._compress_image(relic.image.path)
                    if compressed_data:
                        base64_data = base64.b64encode(compressed_data).decode('utf-8')
                        content_type = self._get_content_type(relic.image.name)
                        data['image_base64'] = f"data:{content_type};base64,{base64_data}"
                except Exception as e:
                    print(f"图片压缩处理错误: {e}")
            # 处理病害图像（压缩后返回base64，用于预览）
            disease_images = []
            for disease_image in relic.disease_images.all():
                try:
                    # 压缩病害图像
                    compressed_data = self._compress_image(disease_image.image.path)
                    if compressed_data:
                        base64_data = base64.b64encode(compressed_data).decode('utf-8')
                        content_type = self._get_content_type(disease_image.image.name)
                        disease_images.append({
                            'id': disease_image.id,
                            'image_base64': f"data:{content_type};base64,{base64_data}",
                            'image_url': disease_image.image.url,
                            # 'created_at': disease_image.created_at.strftime('%Y-%m-%d %H:%M:%S'),
                            'created_at': timezone.localtime(disease_image.created_at).strftime('%Y-%m-%d %H:%M:%S'),
                            'image_name': disease_image.image_name,
                            'image_description': disease_image.description,
                        })
                except Exception as e:
                    print(f"病害图片压缩处理错误: {e}")

            data['disease_images'] = disease_images

            # 处理光谱数据文件（只返回元信息，不返回内容）
            if relic.spectrum_file:
                data['spectrum_file_url'] = request.build_absolute_uri(relic.spectrum_file.url)
                data['spectrum_file_name'] = relic.spectrum_file.name.split('/')[-1]
                data['spectrum_file_size'] = relic.spectrum_file.size

            # 处理三维数据文件（只返回元信息，不返回内容）
            if relic.threeD_file:
                data['threeD_file_url'] = request.build_absolute_uri(relic.threeD_file.url)
                data['threeD_file_name'] = relic.threeD_file.name.split('/')[-1]
                data['threeD_file_size'] = relic.threeD_file.size

            return Response(data, status=200)
        except CulturalRelic.DoesNotExist:
            return Response({
                "error": "文物不存在",
                "message": "指定的文物记录不存在",
                "code": "RELIC_NOT_FOUND"
            }, status=404)

    def _compress_image(self, image_path, quality=60, max_size=(400, 300)):
        """
        压缩图片
        - image_path: 图片路径
        - quality: 压缩质量 (0-100)，默认70
        - max_size: 最大尺寸 (宽, 高)，默认(400, 300)
        """
        try:
            # 打开图片
            image = Image.open(image_path)

            # 转换为RGB模式（处理PNG等格式）
            if image.mode not in ('L', 'RGB'):
                image = image.convert('RGB')

            # 调整图片尺寸
            if image.size[0] > max_size[0] or image.size[1] > max_size[1]:
                image.thumbnail(max_size, Image.LANCZOS)

            # 保存到内存中
            buffer = BytesIO()
            image_format = image.format or 'JPEG'
            image.save(buffer, format=image_format, quality=quality)

            # 返回压缩后的数据
            return buffer.getvalue()
        except Exception as e:
            print(f"图片压缩失败: {e}")
            return None

    def _get_content_type(self, file_name):
        """获取文件的Content-Type"""
        file_ext = file_name.lower().split('.')[-1]
        if file_ext == 'png':
            return 'image/png'
        elif file_ext == 'gif':
            return 'image/gif'
        else:
            return 'image/jpeg'

    def put(self, request, pk):
        try:
            relic = CulturalRelic.objects.get(id=pk)
            if not request.user.is_staff and relic.created_by != request.user:
                return Response({
                    "error": "权限不足",
                    "message": "无权更新该文物记录",
                    "code": "PERMISSION_DENIED"
                }, status=403)

            old_files = {
                'image': relic.image,
                'spectrum_file': relic.spectrum_file,
                'threeD_file': relic.threeD_file,
            }
            # 获取前端传来的 hasSpectrum 和 hasThreeD（假设是 JSON 数据）
            try:
                # 如果是 multipart/form-data，hasSpectrum 可能在 request.POST 中
                has_spectrum = request.POST.get('hasSpectrum', 'false').lower() == 'true'
                has_three_d = request.POST.get('hasThreeD', 'false').lower() == 'true'
            except:
                has_spectrum = False
                has_three_d = False

            # 序列化主模型（部分更新）
            serializer = CulturalRelicSerializer(relic, data=request.data, partial=True)
            if not serializer.is_valid():
                formatted_errors = format_serializer_errors(serializer.errors)
                return Response({
                    "error": "数据验证失败",
                    "message": "提交的数据格式不正确",
                    "code": "VALIDATION_ERROR",
                    "details": formatted_errors
                }, status=400)

            with transaction.atomic():
                # 1. 先保存文物基本信息
                # serializer.save(last_modified_by=request.user)
                updated_relic = serializer.save(last_modified_by=request.user)

                # 2. 获取病害图像元数据列表
                try:
                    metadata_list = json.loads(request.POST.get('disease_image_metadata', '[]'))
                except json.JSONDecodeError:
                    return Response({
                        "error": "元数据格式错误",
                        "message": "病害图像元数据格式错误",
                        "code": "JSON_DECODE_ERROR"
                    }, status=400)

                # 3. 遍历每个元数据，执行更新或创建
                for meta in metadata_list:
                    image_id = meta.get('id')
                    image_name = meta.get('name', '未命名')
                    description = meta.get('description', '')
                    file_key = meta.get('file_key')  # 前端上传文件时使用的字段名

                    # 如果是新增的病害图像（id为None）
                    if image_id is None:
                        if not image_name or not file_key or file_key not in request.FILES:
                            continue  # 跳过无效项

                        new_file = request.FILES[file_key]
                        disease_img = DiseaseImage(
                            relic=relic,
                            image_name=image_name,
                            description=description,
                            created_at=timezone.now(),
                            image=new_file
                        )
                        disease_img.save()

                    else:
                        try:
                            disease_img = DiseaseImage.objects.get(id=image_id, relic=relic)
                            old_image_path = disease_img.image.path if disease_img.image else None

                            # 更新文字信息
                            disease_img.image_name = image_name
                            disease_img.description = description

                            # 如果有新文件上传（通过 file_key）
                            if file_key:
                                new_file = request.FILES.get(file_key)
                                if new_file:
                                    # 删除旧文件
                                    if old_image_path and os.path.exists(old_image_path):
                                        os.remove(old_image_path)
                                    # 保存新文件
                                    disease_img.image.save(new_file.name, new_file, save=False)

                                    # 更新 created_at 字段（或者使用 updated_at）
                                    disease_img.created_at = timezone.now()  # 注意：如果 auto_now_add=True 则此行无效

                            # 保存模型
                            disease_img.save()

                        except ObjectDoesNotExist:
                            # 忽略不存在的图像
                            continue

                # 4. 清理主模型旧文件（如封面图等）
                self._cleanup_old_files(relic, old_files)

                # ✅ 新增：根据 hasSpectrum/hasThreeD 删除文件
                self._delete_conditional_files(updated_relic, has_spectrum, has_three_d)

                # ✅ 成功：只返回成功状态
                return Response({
                    "message": "文物信息更新成功"
                }, status=200)

        except CulturalRelic.DoesNotExist:
            return Response({
                "error": "文物不存在",
                "message": "指定的文物记录不存在",
                "code": "RELIC_NOT_FOUND"
            }, status=404)
        except Exception as e:
            print_exc()
            return Response({
                "error": "更新失败",
                "message": f"更新文物信息时发生错误: {str(e)}",
                "code": "UPDATE_FAILED"
            }, status=500)

    # ✅ 新增：清理旧文件的方法
    def _cleanup_old_files(self, relic, old_files):
        """
        删除旧文件（仅当新文件已成功保存时）
        """
        # 安全获取当前文件路径（只在文件存在时才取 .path）
        current_files = {
            'image': relic.image.path if relic.image and hasattr(relic.image, 'path') else None,
            'spectrum_file': relic.spectrum_file.path if relic.spectrum_file and hasattr(relic.spectrum_file, 'path') else None,
            'threeD_file': relic.threeD_file.path if relic.threeD_file and hasattr(relic.threeD_file, 'path') else None,
        }

        for field_name, old_path in old_files.items():
            current_path = current_files.get(field_name)

            # 确保 old_path 是字符串路径（而不是 ImageFieldFile 对象）
            if old_path and hasattr(old_path, 'path'):
                old_path = old_path.path  # 转为真实路径

            # 只有当 old_path 是有效字符串路径，且与当前路径不同，并且文件存在时，才删除
            if (
                    old_path and
                    isinstance(old_path, str) and
                    old_path != current_path and
                    os.path.exists(old_path)
            ):
                try:
                    os.remove(old_path)
                    print(f"已删除旧文件: {old_path}")
                except Exception as e:
                    print(f"删除旧文件失败 {old_path}: {e}")

    def _delete_conditional_files(self, relic, has_spectrum, has_three_d):
        """
        根据 has_spectrum 和 has_three_d 的值，决定是否删除对应的文件，并清空数据库字段
        """
        updated = False

        # 删除 spectrum_file
        if not has_spectrum and relic.spectrum_file:
            relic.spectrum_file.delete(save=False)  # 删除文件，清空字段
            updated = True

        # 删除 threeD_file
        if not has_three_d and relic.threeD_file:
            relic.threeD_file.delete(save=False)  # 删除文件，清空字段
            updated = True

        # 如果有字段被修改，保存模型
        if updated:
            relic.save(update_fields=['spectrum_file', 'threeD_file', 'last_modified_by'])

    def delete(self, request, pk):
        """删除文物（添加事务处理确保原子性）"""
        try:
            with transaction.atomic():  # 事务包裹，确保文件删除和数据库操作一致性
                relic = CulturalRelic.objects.get(id=pk)

                # 权限检查（统一使用role字段判断管理员，与其他接口逻辑保持一致）
                if not request.user.role == 'admin' and relic.created_by != request.user:
                    return Response({
                        "error": "权限不足",
                        "message": "无权删除该文物记录",
                        "code": "PERMISSION_DENIED"
                    }, status=403)

                # 删除关联文件（修复核心错误点）
                self._delete_relic_files(relic)

                # 删除文物记录
                relic.delete()

                return Response({
                    "message": "文物删除成功"
                }, status=200)

        except CulturalRelic.DoesNotExist:
            return Response({
                "error": "文物不存在",
                "message": "指定的文物记录不存在",
                "code": "RELIC_NOT_FOUND"
            }, status=404)
        except Exception as e:
            # 打印错误日志便于调试
            import traceback
            traceback.print_exc()
            return Response({
                "error": "删除失败",
                "message": f"删除文物时发生错误: {str(e)}",
                "code": "DELETE_FAILED"
            }, status=500)

    def _delete_relic_files(self, relic):
        """删除文物关联的所有文件（修复病害图像删除逻辑）"""
        # 1. 删除文物主图
        if relic.image and os.path.exists(relic.image.path):
            try:
                os.remove(relic.image.path)
            except Exception as e:
                print(f"删除主图失败: {e}")
                # 主图删除失败不阻断后续操作，仅记录日志

        # 2. 删除所有关联的病害图像（修复核心错误：遍历disease_images而非访问不存在的disease_image）
        for disease_image in relic.disease_images.all():
            if disease_image.image and os.path.exists(disease_image.image.path):
                try:
                    os.remove(disease_image.image.path)
                except Exception as e:
                    print(f"删除病害图像[{disease_image.id}]失败: {e}")
            # 删除病害图像数据库记录
            disease_image.delete()

        # 3. 删除光谱文件
        if relic.spectrum_file and os.path.exists(relic.spectrum_file.path):
            try:
                os.remove(relic.spectrum_file.path)
            except Exception as e:
                print(f"删除光谱文件失败: {e}")

        # 4. 删除3D文件
        if relic.threeD_file and os.path.exists(relic.threeD_file.path):
            try:
                os.remove(relic.threeD_file.path)
            except Exception as e:
                print(f"删除3D文件失败: {e}")